<?php
/**
 * 守护程序
 * 系统会通过计划任务执行php.exe daemon.php周期性（T<20min）启动本程序，实现循环处理   
 * 考虑到OA消息的实时性和短信发送的时间段限制，发送时间基于以下考虑：
 * 1. 在8:20-21:00时间段，读取当前时间前20分钟的短信并发送。
 * 2. 在21:00-8:00时间段的短信，也可以请求成功，但会在8:00以后才发送出去。
 * 因此，实际上可以24小时采用同一种处理方式：每次启动都读取最近20分钟的短信，然后发送。  
 * 对于请求失败的短信，在几次请求后也已经到了过期时间，因此也避免了重复请求的问题。  
 */

include_once("./MMysql.php");
include_once("./Log.php");
include_once("./vendor/autoload.php"); //composer自动加载
// 如果生产环境配置文件存在就使用生产环境的，为了便于库的管理且防止关键配置泄露
// 请将config.production.php加入.gitignore中，这样开源的时候修改config.php作为样例即可
if(file_exists("./config.production.php")){ 
    include_once("./config.production.php");
}else{
    include_once("./config.php");
}


use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Ecs\Ecs;
use MyLog\Log as Log;

$LOG= Log::getInstance(array(
            'LOG_FILE'=>$_CFG['sys']['log'], 
            'LOG_LEVEL'=>75 //INFO
        ));
$LOG->logMessage("====正在运行====",Log::NOTICE,null);

//用于回填到SEND_NUM中标记该条短信无法发送，避免下次又读取
$gErrFill = $_CFG['sys']['ErrFill']; //数据问题不予发送
$gErrApi  = $_CFG['sys']['ErrApi']; //api请求失败 

$mysql = new MMysql($_CFG['db']);

//查询 
$validTime = date("Y-m-d H:i:s", strtotime("-{$_CFG['sys']['overdueMinutes']} minute")); //有效期内的短信

$sms2 = $mysql 
    ->limit(1,10)
    ->order(['SMS_ID'=>'desc'])
    ->where([
            'SEND_TIME' => ["'$validTime'",'>'],  //注意双引号
            'SEND_NUM' => '1'  
        ] )
    //->where(['SEND_NUM' => '1']) //注意这里不能再写where，不支持多个where
    ->select('sms2');

$LOG->logMessage('待发短信总数：'.count($sms2));
// return;

// 没有数据就结束
if(!count($sms2)){
    return true;
}

// 有数据就发送
foreach ($sms2 as $resIdx => $sms) {
    //判断短信内容并解析出模板变量
    $type = '';
    $data = [];
    $content = $sms['CONTENT'];
    $phone = trim($sms['PHONE']);
    $LOG->logMessage("SMS_ID:{$sms['SMS_ID']},PHONE:$phone,CONTENT:$content");
    if(!isValidPhone($phone)){
        $LOG->logMessage('非法的手机号码(不予发送)： '.$phone);
        //标记以避免再次读取
        $update = $mysql->where([ 'SMS_ID'=>$sms['SMS_ID']]  ) 
                        ->update('sms2',[ 'SEND_NUM'=> $gErrFill ] );
        continue;
    }
    if (strpos($content,'您有新的工作需要办理，流水号：')!==false){
        $type = 'NEW_WORK';
        $data = parseContent($type,$content);
        if( (int)$data['liushuihao']==0 || empty($data['mingcheng']) ) continue;
    }
    elseif (strpos($content,'工作流退回提醒：')!==false){
        $type = 'BACK_WORK';
        $data = parseContent($type,$content);
        if( empty($data['liushuihao']) ) continue;
    }
    elseif (strpos($content,'工作已结束，流水号')) {
        $type = 'END_WORK';
        $data = parseContent($type,$content);
        if( (int)$data['liushuihao']==0 ) continue;
    }
    elseif (strpos($content,'尽快进行工作办理')) {
        $type = 'URGE_WORK';
        $data = parseContent($type,$content);
    }
    elseif (strpos($content,'项目超时提醒，请及时关注')) {
        $type = 'TIMEOUT_WORK';
        $data = parseContent($type,$content);
        if( (int)$data['liushuihao']==0 ) continue;
    }
    elseif (strpos($content,'您好，您有未办事宜：')) {  // 系统消息：${name}您好，您有未办事宜：${content}。
        $type = 'TODO_WORK';
        $data = parseContent($type,$content);
        if( !$data['name'] ) continue;
    }
    else{
        $LOG->logMessage('无法解析的短信类型');
        //标记以避免再次读取
        $update = $mysql->where([ 'SMS_ID'=>$sms['SMS_ID']]  ) 
                        ->update('sms2',[ 'SEND_NUM'=> $gErrFill ] );
        continue;
    }
    $LOG->logMessage('解析后得到的数据：'.json_encode($data,JSON_UNESCAPED_UNICODE));
    $LOG->logMessage('判断为消息类型：'.$type);
    if( empty($type) ) continue; 
    
    //调用sendSms发送
    $sendRes = sendSms( $phone, $_CFG['dysms']['CODE'][$type],  $data );
    $LOG->logMessage(json_encode($sendRes,JSON_UNESCAPED_UNICODE));
    if($sendRes['Code'] == 'OK'){
        //根据返回结果修改sms2表记录的发送状态  修改SEND_NUM+1
        $update = $mysql->where([ 'SMS_ID'=>$sms['SMS_ID']]  ) 
                        ->update('sms2',[ 'SEND_NUM'=>$sms['SEND_NUM']+1 ] );
    }else{
        $LOG->logMessage("api：发送失败");
        //标记以避免再次读取
        $update = $mysql->where([ 'SMS_ID'=>$sms['SMS_ID']]  ) 
                        ->update('sms2',[ 'SEND_NUM'=>$gErrApi ] );
        continue;
    }

}

/**
 * 从字符串中解析出内容  
 * @param string type 解析类别（各类通知）,不同的模板不一样，
 * @param string str  被解析的字符串
 */
function parseContent($type,$str){
    $ret = [];
    switch ($type) {
        case 'NEW_WORK':   //您有新的工作需要办理，流水号：5819，工作名称/文号：教师调课
            $str1 = '您有新的工作需要办理，流水号：';
            $str2 = '，工作名称/文号：';
            $pos2 = strpos($str,$str2);
            $ret['liushuihao'] = substr($str,strlen($str1),$pos2-strlen($str1));
            $ret['mingcheng'] = substr($str, $pos2+ strlen($str2));
            break;
        case 'BACK_WORK':   //工作流退回提醒：流水号5814的工作退回给您处理
            $str1 = '工作流退回提醒：流水号';
            $str2 = '的工作退回';
            $pos2 = strpos($str,$str2);
            $ret['liushuihao'] = substr($str,strlen($str1),$pos2-strlen($str1));
            break;
        case 'END_WORK':   //工作已结束，流水号：5826，工作名称/文号：加工17GW2班级校外跟岗实习申请
            $str1 = '工作已结束，流水号：';
            $str2 = '，工作名称/文号：';
            $pos2 = strpos($str,$str2);
            $ret['liushuihao'] = substr($str,strlen($str1),$pos2-strlen($str1));
            $ret['mingcheng'] = substr($str, $pos2+ strlen($str2));
            break;
        case 'URGE_WORK':   //[系统管理员]催办[罗毅]尽快进行工作办理
            $ret = []; //该短信没有参数，注意发短信内容和这里的内容不一样
            break;
        case 'TIMEOUT_WORK':   //项目超时提醒，请及时关注！
            $ret = []; //该短信没有参数，
            break;
        case 'TODO_WORK':   //系统消息：${name}您好，您有未办事宜：${content}。
            $str1 = '系统消息：';
            $str2 = '您好，您有未办事宜：';
            $pos2 = strpos($str,$str2);
            $ret['name'] = substr($str,strlen($str1),$pos2-strlen($str1));
            $ret['content'] = substr($str, $pos2+ strlen($str2));            
            break;
        default:
            # code...
            break;
    }
    return $ret;
}

/**
 * 发短信
 * @param phone电话号码， tmplCode模板代码（阿里云后台获取），data短信模板参数
 * @return 返回阿里云的结果
 */
function sendSms(  $phone,$tmplCode,$data=[]){
    foreach ($data as $k => $v) { //阿里云sms不允许参数>20,所以要截取
        if(strlen($v)>20) $data[$k] = mb_substr($v,0,18) . '..';
    }
    if(!empty($data))  $param = json_encode($data,JSON_UNESCAPED_UNICODE);
    global $_CFG,$LOG;
    AlibabaCloud::accessKeyClient( 
        $_CFG['dysms']['AccessKeyID'], 
        $_CFG['dysms']['AccessKeySecret'])
    ->regionId( $_CFG['dysms']['RegionId'] )  
    ->asDefaultClient();
    $options = [
        'query' => [
            'RegionId' => "default",
            'PhoneNumbers' => $phone,
            'SignName' => $_CFG['dysms']['SignName'],
            'TemplateCode' => $tmplCode,
        ],
    ];
    if(!empty($data)) $options['query']['TemplateParam'] = $param;
    $LOG->logMessage(json_encode($options,JSON_UNESCAPED_UNICODE));
    try {
        $result = AlibabaCloud::rpc()
                    ->product('Dysmsapi')
                    // ->scheme('https') // https | http
                    ->version('2017-05-25')
                    ->action('SendSms')
                    ->method('POST')
                    ->host( $_CFG['dysms']['host'] )
                    ->options($options)
                    ->request();
        return $result->toArray();
    } catch (ClientException $e) {
        $LOG->logMessage( $e->getErrorMessage() . PHP_EOL ); //原来是echo，可能会导致异常，所以转为Log文件记录
    } catch (ServerException $e) {
        $LOG->logMessage( $e->getErrorMessage() . PHP_EOL );
    }
}


/**
 * 验证手机号合法性
 * @param string phone 手机号
 * @return boolean true or false
 */
function isValidPhone($phone=''){
    if(preg_match("/^1[3456789]{1}\d{9}$/",$phone)){
        return true;
    }
    return false;
}